{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 量子分类器\n",
    "\n",
    "<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概览\n",
    "\n",
    "本教程我们将讨论量子分类器（quantum classifier）的原理，以及如何利用量子神经网络（quantum neural network, QNN）来完成**二分类**任务。这类方法早期工作的主要代表是 Mitarai et al.(2018) 的量子电路学习 [(Quantum Circuit Learning, QCL)](https://arxiv.org/abs/1803.00745) [1], Farhi & Neven (2018) [2] 和 Schuld et al.(2018) 的中心电路量子分类器 [Circuit-Centric Quantum Classifiers](https://arxiv.org/abs/1804.00633) [3]。这里我们以第一类的 QCL 框架应用于监督学习（Supervised learning）为例进行介绍，通常我们需要先将经典数据编码成量子数据，然后通过训练量子神经网络的参数，最终得到一个最优的分类器。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 背景\n",
    "\n",
    "在监督学习的情况下，我们需要输入 $N$ 个带标签的数据点构成的数据集 $D = \\{(x^k,y^k)\\}_{k=1}^{K}$，其中 $x^k\\in \\mathbb{R}^{m}$ 是数据点，$y^k \\in\\{0,1\\}$ 是对应数据点 $x^k$ 的分类标签。**分类过程实质上是一个决策过程，决策给定数据点的标签归属问题**。 对于量子分类器框架，分类器 $\\mathcal{F}$ 的实现方式为一个含参 $\\theta$ 的量子神经网络/参数化量子电路, 测量量子系统以及数据后处理的组合。一个优秀的分类器 $\\mathcal{F}_\\theta$ 应该尽可能的将每个数据集内的数据点正确地映射到相对应的标签上 $\\mathcal{F}_\\theta(x^k) \\rightarrow y^k$。因此，我们将预测标签 $\\tilde{y}^{k} = \\mathcal{F}_\\theta(x^k)$ 和实际标签 $y^k$ 之间的累计距离作为损失函数 $\\mathcal{L}(\\theta)$ 进行优化。对于两分类任务，可以选择二次损失函数\n",
    "\n",
    "$$\n",
    "\\mathcal{L}(\\theta) = \\sum_{k=1}^N 1/N \\cdot |\\tilde{y}^{k}-y^k|^2. \\tag{1}\n",
    "$$\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 方案流程\n",
    "\n",
    "这里我们给出实现量子电路学习 (QCL) 框架下量子分类器的一个流程。\n",
    "\n",
    "1. 将经典数据编码$x^k$为量子数据$\\lvert \\psi_{\\rm in}\\rangle^k$。本教程采用角度编码。关于编码方式的具体操作，见[量子态编码经典数据](./DataEncoding_CN.ipynb)。用户也可以尝试其他编码，如振幅编码，体验不同编码方式对分类器学习效率的影响。\n",
    "2. 构建可调参数量子电路，对应幺正变换(unitary gate)$U(\\theta)$。\n",
    "3. 对每一个量子数据$\\lvert\\psi_{\\rm in}\\rangle^k$，通过参数化量子电路$U(\\theta)$，得到输出态$\\lvert \\psi_{\\rm out}\\rangle^k = U(\\theta)\\lvert \\psi_{\\rm in} \\rangle^k$。\n",
    "4. 对每一个量子数据得到的输出量子态$\\lvert \\psi_{\\rm out}\\rangle^k$，通过测量与数据后处理，得到标签 $\\tilde{y}^{k}$。\n",
    "5. 重复以上步骤，得到数据集内所有点的标签，并计算损失函数 $\\mathcal{L}(\\theta)$。\n",
    "6. 通过梯度下降等优化方法不断调整参数 $\\theta$ 的值，从而最小化损失函数。记录优化完成后的最优参数 $\\theta^*$, 这时我们就学习到了最优的分类器 $\\mathcal{F}_{\\theta^*}$。\n",
    "\n",
    "<img src=\"./figures/qclassifier-fig-pipeline-cn.png\" width=\"700px\" /> \n",
    "<center> 图 1：量子分类器训练的流程图 </center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paddle Quantum 实现\n",
    "\n",
    "这里，我们先导入所需要的语言包："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-02T09:15:03.419838Z",
     "start_time": "2021-03-02T09:15:03.413324Z"
    }
   },
   "outputs": [],
   "source": [
    "# 导入 numpy、paddle 和 paddle_quantum\n",
    "import numpy as np\n",
    "import paddle\n",
    "import paddle_quantum\n",
    "\n",
    "# 构建量子电路\n",
    "from paddle_quantum.ansatz import Circuit\n",
    "\n",
    "# 一些用到的函数\n",
    "from numpy import pi as PI\n",
    "from paddle import matmul, transpose, reshape  # paddle 矩阵乘法与转置\n",
    "from paddle_quantum.qinfo import pauli_str_to_matrix  # 得到 N 量子比特泡利矩阵,\n",
    "from paddle_quantum.linalg import dagger # 复共轭\n",
    "\n",
    "# 作图与计算时间\n",
    "from matplotlib import pyplot as plt\n",
    "import time"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "分类器问题用到的参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据集参数设置\n",
    "Ntrain = 200        # 规定训练集大小\n",
    "Ntest = 100         # 规定测试集大小\n",
    "boundary_gap = 0.5  # 设置决策边界的宽度\n",
    "seed_data = 2       # 固定随机种子\n",
    "# 训练参数设置\n",
    "N = 4               # 所需的量子比特数量\n",
    "DEPTH = 1           # 采用的电路深度\n",
    "BATCH = 20          # 训练时 batch 的大小\n",
    "EPOCH = int(200 * BATCH / Ntrain)\n",
    "                    # 训练 epoch 轮数，使得总迭代次数 EPOCH * (Ntrain / BATCH) 在200左右\n",
    "LR = 0.01           # 设置学习速率\n",
    "seed_paras = 19     # 设置随机种子用以初始化各种参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据集的生成\n",
    "\n",
    "对于监督学习来说，我们绕不开的一个问题就是——采用什么样的数据集呢？在这个教程中我们按照论文 [1] 里所提及方法生成简单的圆形决策边界二分数据集 $\\{(x^{k}, y^{k})\\}$。其中数据点 $x^{k}\\in \\mathbb{R}^{2}$，标签 $y^{k} \\in \\{0,1\\}$。\n",
    "\n",
    "<img src=\"./figures/qclassifier-fig-data-cn.png\" width=\"400px\" /> \n",
    "<center> 图 2：生成的数据集和对应的决策边界 </center>\n",
    "\n",
    "具体的生成方式和可视化请见如下代码："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据集生成函数 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-02T09:15:04.631031Z",
     "start_time": "2021-03-02T09:15:04.617301Z"
    }
   },
   "outputs": [],
   "source": [
    "# 圆形决策边界两分类数据集生成器\n",
    "def circle_data_point_generator(Ntrain, Ntest, boundary_gap, seed_data):\n",
    "    \"\"\"\n",
    "    :param Ntrain: 训练集大小\n",
    "    :param Ntest: 测试集大小\n",
    "    :param boundary_gap: 取值于 (0, 0.5), 两类别之间的差距\n",
    "    :param seed_data: 随机种子\n",
    "    :return: 四个列表：训练集x，训练集y，测试集x，测试集y\n",
    "    \"\"\"\n",
    "    # 生成共Ntrain + Ntest组数据，x对应二维数据点，y对应编号\n",
    "    # 取前Ntrain个为训练集，后Ntest个为测试集\n",
    "    train_x, train_y = [], []\n",
    "    num_samples, seed_para = 0, 0\n",
    "    while num_samples < Ntrain + Ntest:\n",
    "        np.random.seed((seed_data + 10) * 1000 + seed_para + num_samples)\n",
    "        data_point = np.random.rand(2) * 2 - 1  # 生成[-1, 1]范围内二维向量\n",
    "\n",
    "        # 如果数据点的模小于(0.7 - gap)，标为0\n",
    "        if np.linalg.norm(data_point) < 0.7 - boundary_gap / 2:\n",
    "            train_x.append(data_point)\n",
    "            train_y.append(0.)\n",
    "            num_samples += 1\n",
    "\n",
    "        # 如果数据点的模大于(0.7 + gap)，标为1\n",
    "        elif np.linalg.norm(data_point) > 0.7 + boundary_gap / 2:\n",
    "            train_x.append(data_point)\n",
    "            train_y.append(1.)\n",
    "            num_samples += 1\n",
    "        else:\n",
    "            seed_para += 1\n",
    "\n",
    "    train_x = np.array(train_x).astype(\"float64\")\n",
    "    train_y = np.array([train_y]).astype(\"float64\").T\n",
    "\n",
    "    print(\"训练集的维度大小 x {} 和 y {}\".format(np.shape(train_x[0:Ntrain]), np.shape(train_y[0:Ntrain])))\n",
    "    print(\"测试集的维度大小 x {} 和 y {}\".format(np.shape(train_x[Ntrain:]), np.shape(train_y[Ntrain:])), \"\\n\")\n",
    "\n",
    "    return train_x[0:Ntrain], train_y[0:Ntrain], train_x[Ntrain:], train_y[Ntrain:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据集可视化函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 用以可视化生成的数据集\n",
    "def data_point_plot(data, label):\n",
    "    \"\"\"\n",
    "    :param data: 形状为 [M, 2], 代表M个 2-D 数据点\n",
    "    :param label: 取值 0 或者 1\n",
    "    :return: 画这些数据点\n",
    "    \"\"\"\n",
    "    dim_samples, dim_useless = np.shape(data)\n",
    "    plt.figure(1)\n",
    "    for i in range(dim_samples):\n",
    "        if label[i] == 0:\n",
    "            plt.plot(data[i][0], data[i][1], color=\"r\", marker=\"o\")\n",
    "        elif label[i] == 1:\n",
    "            plt.plot(data[i][0], data[i][1], color=\"b\", marker=\"o\")\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此教程采用大小分别为 200, 100 的训练集，测试集，决策边界宽度为 0.5 的数据，用以训练与测试量子神经网络训练效果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-02T09:15:06.422981Z",
     "start_time": "2021-03-02T09:15:05.043595Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集的维度大小 x (200, 2) 和 y (200, 1)\n",
      "测试集的维度大小 x (100, 2) 和 y (100, 1) \n",
      "\n",
      "训练集 200 个数据点的可视化：\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试集 100 个数据点的可视化：\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 读者不妨自己调节数据集的参数设置来生成属于自己的数据集吧！\n"
     ]
    }
   ],
   "source": [
    "# 生成自己的数据集\n",
    "train_x, train_y, test_x, test_y = circle_data_point_generator(Ntrain, Ntest, boundary_gap, seed_data)\n",
    "\n",
    "# 打印数据集的维度信息\n",
    "print(\"训练集 {} 个数据点的可视化：\".format(Ntrain))\n",
    "data_point_plot(train_x, train_y)\n",
    "print(\"测试集 {} 个数据点的可视化：\".format(Ntest))\n",
    "data_point_plot(test_x, test_y)\n",
    "print(\"\\n 读者不妨自己调节数据集的参数设置来生成属于自己的数据集吧！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据的预处理\n",
    "\n",
    "与经典机器学习不同的是，量子分类器在实际工作的时候需要考虑数据的预处理。我们需要多加一个步骤将经典的数据转化成量子信息才能放在量子计算机上运行。此处我们采用角度编码方式得到量子数据。\n",
    "\n",
    "首先我们确定需要使用的量子比特数量。因为我们的数据 $\\{x^{k} = (x^{k}_0, x^{k}_1)\\}$ 是二维的, 按照 Mitarai (2018) 论文[1]中的编码方式我们至少需要2个量子比特。接着准备一系列的初始量子态 $|00\\rangle$。然后将经典信息 $\\{x^{k}\\}$ 编码成一系列量子门 $U(x^{k})$ 并作用在初始量子态上。最终得到一系列的量子态 $|\\psi_{\\rm in}\\rangle^k = U(x^{k})|00\\rangle$。这样我们就完成从经典信息到量子信息的编码了！\n",
    "\n",
    "给定 $m$ 个量子比特去编码二维的经典数据点，采用角度编码，量子门的构造为：\n",
    "\n",
    "$$\n",
    "U(x^{k}) = \\otimes_{j=0}^{m-1} R_j^z\\big[\\arccos(x^{k}_{j \\, \\text{mod} \\, 2}\\cdot x^{k}_{j \\, \\text{mod} \\, 2})\\big] R_j^y\\big[\\arcsin(x^{k}_{j \\, \\text{mod} \\, 2}) \\big],\\tag{2}\n",
    "$$\n",
    "\n",
    "**注意** ：这种表示下，我们将第一个量子比特编号为 $j = 0$。更多编码方式见 [Robust data encodings for quantum classifiers](https://arxiv.org/pdf/2003.01695.pdf)。读者也可以直接使用量桨中提供的[编码方式](./DataEncoding_CN.ipynb)。这里我们也欢迎读者自己创新尝试全新的编码方式。\n",
    "\n",
    "由于这种编码的方式看着比较复杂，我们不妨来举一个简单的例子。假设我们给定一个数据点 $x = (x_0, x_1)= (1,0)$, 显然这个数据点的标签应该为 1，对应上图**蓝色**的点。同时数据点对应的2比特量子门 $U(x)$ 是\n",
    "\n",
    "$$\n",
    "U(x) = \n",
    "\\bigg( R_0^z\\big[\\arccos(x_{0}\\cdot x_{0})\\big] R_0^y\\big[\\arcsin(x_{0}) \\big]  \\bigg)\n",
    "\\otimes \n",
    "\\bigg( R_1^z\\big[\\arccos(x_{1}\\cdot x_{1})\\big] R_1^y\\big[\\arcsin(x_{1}) \\big] \\bigg),\\tag{3}\n",
    "$$\n",
    "\n",
    "\n",
    "把具体的数值带入我们就能得到：\n",
    "$$\n",
    "U(x) = \n",
    "\\bigg( R_0^z\\big[0\\big] R_0^y\\big[\\pi/2 \\big]  \\bigg)\n",
    "\\otimes \n",
    "\\bigg( R_1^z\\big[\\pi/2\\big] R_1^y\\big[0 \\big] \\bigg),\n",
    "\\tag{4}\n",
    "$$\n",
    "\n",
    "以下是常用的旋转门的矩阵形式：\n",
    "\n",
    "\n",
    "$$\n",
    "R_x(\\theta) :=\n",
    "\\begin{bmatrix}\n",
    "\\cos \\frac{\\theta}{2} &-i\\sin \\frac{\\theta}{2} \\\\\n",
    "-i\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2}\n",
    "\\end{bmatrix}\n",
    ",\\quad\n",
    "R_y(\\theta) :=\n",
    "\\begin{bmatrix}\n",
    "\\cos \\frac{\\theta}{2} &-\\sin \\frac{\\theta}{2} \\\\\n",
    "\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2}\n",
    "\\end{bmatrix}\n",
    ",\\quad\n",
    "R_z(\\theta) :=\n",
    "\\begin{bmatrix}\n",
    "e^{-i\\frac{\\theta}{2}} & 0 \\\\\n",
    "0 & e^{i\\frac{\\theta}{2}}\n",
    "\\end{bmatrix}.\n",
    "\\tag{5}\n",
    "$$\n",
    "\n",
    "那么这个两比特量子门 $U(x)$ 的矩阵形式可以写为：\n",
    "\n",
    "$$\n",
    "U(x) = \n",
    "\\bigg(\n",
    "\\begin{bmatrix}\n",
    "1 & 0 \\\\ \n",
    "0 & 1\n",
    "\\end{bmatrix}\n",
    "\\begin{bmatrix}\n",
    "\\cos \\frac{\\pi}{4} &-\\sin \\frac{\\pi}{4} \\\\ \n",
    "\\sin \\frac{\\pi}{4} &\\cos \\frac{\\pi}{4} \n",
    "\\end{bmatrix}\n",
    "\\bigg)\n",
    "\\otimes \n",
    "\\bigg(\n",
    "\\begin{bmatrix}\n",
    "e^{-i\\frac{\\pi}{4}} & 0 \\\\ \n",
    "0 & e^{i\\frac{\\pi}{4}}\n",
    "\\end{bmatrix}\n",
    "\\begin{bmatrix}\n",
    "1 &0 \\\\ \n",
    "0 &1\n",
    "\\end{bmatrix}\n",
    "\\bigg)\\, .\\tag{6}\n",
    "$$\n",
    "\n",
    "化简后我们作用在零初始化的 $|00\\rangle$ 量子态上可以得到编码后的量子态 $|\\psi_{\\rm in}\\rangle$，\n",
    "\n",
    "$$\n",
    "|\\psi_{\\rm in}\\rangle =\n",
    "U(x)|00\\rangle = \\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "1-i &0 &-1+i &0 \\\\ \n",
    "0 &1+i &0  &-1-i \\\\\n",
    "1-i &0 &1-i  &0 \\\\\n",
    "0 &1+i &0  &1+i \n",
    "\\end{bmatrix}\n",
    "\\begin{bmatrix}\n",
    "1 \\\\\n",
    "0 \\\\\n",
    "0 \\\\\n",
    "0\n",
    "\\end{bmatrix}\n",
    "= \\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "1-i \\\\\n",
    "0 \\\\\n",
    "1-i \\\\\n",
    "0\n",
    "\\end{bmatrix}.\\tag{7}\n",
    "$$\n",
    "\n",
    "接着我们来看看代码上怎么实现这种编码方式。需要注意的是：代码中使用了一个张量积来表述\n",
    "\n",
    "$$\n",
    "(U_1 |0\\rangle)\\otimes (U_2 |0\\rangle) = (U_1 \\otimes U_2) |0\\rangle\\otimes|0\\rangle\n",
    "= (U_1 \\otimes U_2) |00\\rangle.\\tag{8}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-02T09:15:06.589265Z",
     "start_time": "2021-03-02T09:15:06.452691Z"
    }
   },
   "outputs": [],
   "source": [
    "# 构建绕 Y 轴，绕 Z 轴旋转 theta 角度矩阵\n",
    "def Ry(theta):\n",
    "    \"\"\"\n",
    "    :param theta: 参数\n",
    "    :return: Y 旋转矩阵\n",
    "    \"\"\"\n",
    "    return np.array([[np.cos(theta / 2), -np.sin(theta / 2)],\n",
    "                     [np.sin(theta / 2), np.cos(theta / 2)]])\n",
    "\n",
    "def Rz(theta):\n",
    "    \"\"\"\n",
    "    :param theta: 参数\n",
    "    :return: Z 旋转矩阵\n",
    "    \"\"\"\n",
    "    return np.array([[np.cos(theta / 2) - np.sin(theta / 2) * 1j, 0],\n",
    "                     [0, np.cos(theta / 2) + np.sin(theta / 2) * 1j]])\n",
    "\n",
    "# 经典 -> 量子数据编码器\n",
    "def datapoints_transform_to_state(data, n_qubits):\n",
    "    \"\"\"\n",
    "    :param data: 形状为 [-1, 2]，numpy向量形式\n",
    "    :param n_qubits: 数据转化后的量子比特数量\n",
    "    :return: 形状为 [-1, 1, 2 ^ n_qubits]\n",
    "            形状中-1表示第一个参数为任意大小。在此教程实例分析中，对应于BATCH，用以得到Eq.(1)中平方误差的平均值\n",
    "    \"\"\"\n",
    "    dim1, dim2 = data.shape\n",
    "    res = []\n",
    "    for sam in range(dim1):\n",
    "        res_state = 1.\n",
    "        zero_state = np.array([[1, 0]])\n",
    "        # 角度编码\n",
    "        for i in range(n_qubits):\n",
    "            # 对偶数编号量子态作用 Rz(arccos(x0^2)) Ry(arcsin(x0))\n",
    "            if i % 2 == 0:\n",
    "                state_tmp=np.dot(zero_state, Ry(np.arcsin(data[sam][0])).T)\n",
    "                state_tmp=np.dot(state_tmp, Rz(np.arccos(data[sam][0] ** 2)).T)\n",
    "                res_state=np.kron(res_state, state_tmp)\n",
    "            # 对奇数编号量子态作用 Rz(arccos(x1^2)) Ry(arcsin(x1))\n",
    "            elif i % 2 == 1:\n",
    "                state_tmp=np.dot(zero_state, Ry(np.arcsin(data[sam][1])).T)\n",
    "                state_tmp=np.dot(state_tmp, Rz(np.arccos(data[sam][1] ** 2)).T)\n",
    "                res_state=np.kron(res_state, state_tmp)\n",
    "        res.append(res_state)\n",
    "    res = np.array(res, dtype=paddle_quantum.get_dtype())\n",
    "\n",
    "    return res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "测试角度编码下得到的量子数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "作为测试我们输入以上的经典信息:\n",
      "(x_0, x_1) = (1, 0)\n",
      "编码后输出的2比特量子态为:\n",
      "[[[0.5-0.5j 0. +0.j  0.5-0.5j 0. +0.j ]]]\n"
     ]
    }
   ],
   "source": [
    "print(\"作为测试我们输入以上的经典信息:\")\n",
    "print(\"(x_0, x_1) = (1, 0)\")\n",
    "print(\"编码后输出的2比特量子态为:\")\n",
    "print(datapoints_transform_to_state(np.array([[1, 0]]), n_qubits=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 构造量子神经网络\n",
    "\n",
    "那么在完成上述从经典数据到量子数据的编码后，我们现在可以把这些量子态输入到量子计算机里面了。在那之前，我们还需要设计下我们所采用的量子神经网络结构。\n",
    "\n",
    "<img src=\"./figures/qclassifier-fig-circuit.png\" width=\"600px\" /> \n",
    "<center> 图 3：参数化量子神经网络的电路结构 </center>\n",
    "\n",
    "为了方便，我们统一将上述参数化的量子神经网络称为 $U(\\boldsymbol{\\theta})$。这个 $U(\\boldsymbol{\\theta})$ 是我们分类器的关键组成部分，需要一定的复杂结构来拟合我们的决策边界。与经典神经网络类似，量子神经网络的的设计并不是唯一的，这里展示的仅仅是一个例子，读者不妨自己设计出自己的量子神经网络。我们还是拿原来提过的这个数据点 $x = (x_0, x_1)= (1,0)$ 来举例子，编码过后我们已经得到了一个量子态 $|\\psi_{\\rm in}\\rangle$，\n",
    "\n",
    "$$\n",
    "|\\psi_{\\rm in}\\rangle =\n",
    "\\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "1-i \\\\\n",
    "0 \\\\\n",
    "1-i \\\\\n",
    "0\n",
    "\\end{bmatrix},\\tag{9}\n",
    "$$\n",
    "\n",
    "接着我们把这个量子态输入进我们的量子神经网络，也就是把一个酉矩阵乘以一个向量。得到处理过后的量子态 $|\\psi_{\\rm out}\\rangle$\n",
    "\n",
    "$$\n",
    "|\\psi_{\\rm out}\\rangle = U(\\boldsymbol{\\theta})|\\psi_{\\rm in}\\rangle,\\tag{10}\n",
    "$$\n",
    "\n",
    "如果我们把所有的参数 $\\theta$ 都设置为 $\\theta = \\pi$, 那么我们就可以写出具体的矩阵了：\n",
    "\n",
    "$$\n",
    "|\\psi_{\\rm out}\\rangle = \n",
    "U(\\boldsymbol{\\theta} =\\pi)|\\psi_{\\rm in}\\rangle =\n",
    "\\begin{bmatrix}\n",
    "0  &0 &-1 &0 \\\\ \n",
    "-1 &0 &0  &0 \\\\\n",
    "0  &1 &0  &0 \\\\\n",
    "0  &0 &0  &1 \n",
    "\\end{bmatrix}\n",
    "\\cdot\n",
    "\\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "1-i \\\\\n",
    "0 \\\\\n",
    "1-i \\\\\n",
    "0\n",
    "\\end{bmatrix}\n",
    "= \\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "-1+i \\\\\n",
    "-1+i \\\\\n",
    "0 \\\\\n",
    "0\n",
    "\\end{bmatrix}.\\tag{11}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 测量\n",
    "\n",
    "经过量子神经网络$U(\\theta)$后，得到是量子态$\\lvert \\psi_{\\rm out}\\rangle^k = U(\\theta)\\lvert \\psi_{\\rm in} \\rangle^k$。要想得到该量子态的标签，我们需要通过测量来得到经典信息。然后再通过这些处理后的经典信息计算损失函数 $\\mathcal{L}(\\boldsymbol{\\theta})$。最后再通过梯度下降算法来不断更新 QNN 参数 $\\boldsymbol{\\theta}$，并优化损失函数。\n",
    "\n",
    "\n",
    "这里我们采用的测量方式是测量泡利 $Z$ 算符在第一个量子比特上的期望值。 具体来说，\n",
    "\n",
    "$$\n",
    "\\langle Z \\rangle = \n",
    "\\langle \\psi_{\\rm out} |Z\\otimes I\\cdots \\otimes I| \\psi_{\\rm out}\\rangle,\\tag{12}\n",
    "$$\n",
    "\n",
    "复习一下，泡利 $Z$ 算符的矩阵形式为：\n",
    "\n",
    "$$\n",
    "Z := \\begin{bmatrix} 1 &0 \\\\ 0 &-1 \\end{bmatrix},\\tag{13}\n",
    "$$\n",
    "\n",
    "继续我们前面的 2 量子比特的例子，测量过后我们得到的期望值就是：\n",
    "$$\n",
    "\\langle Z \\rangle = \n",
    "\\langle \\psi_{\\rm out} |Z\\otimes I| \\psi_{\\rm out}\\rangle = \n",
    "\\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "-1-i \\quad\n",
    "-1-i \\quad\n",
    "0   \\quad\n",
    "0\n",
    "\\end{bmatrix}\n",
    "\\begin{bmatrix}\n",
    "1  &0 &0  &0 \\\\ \n",
    "0  &1 &0  &0 \\\\\n",
    "0  &0 &-1 &0 \\\\\n",
    "0  &0 &0  &-1 \n",
    "\\end{bmatrix}\n",
    "\\cdot\n",
    "\\frac{1}{2}\n",
    "\\begin{bmatrix}\n",
    "-1+i \\\\\n",
    "-1+i \\\\\n",
    "0 \\\\\n",
    "0\n",
    "\\end{bmatrix}\n",
    "= 1,\\tag{14}\n",
    "$$\n",
    "\n",
    "好奇的读者或许会问，这个测量结果好像就是我们原来的标签 1 ，这是不是意味着我们已经成功的分类这个数据点了？其实并不然，因为 $\\langle Z \\rangle$ 的取值范围通常在 $[-1,1]$之间。 为了对应我们的标签范围 $y^{k} \\in \\{0,1\\}$, 我们还需要将区间上下限映射上。这个映射最简单的做法就是让\n",
    "\n",
    "$$\n",
    "\\tilde{y}^{k} = \\frac{\\langle Z \\rangle}{2} + \\frac{1}{2} + bias \\quad \\in [0, 1].\\tag{15}\n",
    "$$\n",
    "\n",
    "其中加入偏置（bias）是机器学习中的一个小技巧，目的就是为了让决策边界不受制于原点或者一些超平面。一般我们默认偏置初始化为0，并且优化器在迭代过程中会类似于参数 $\\theta$ 一样不断更新偏置确保 $\\tilde{y}^{k} \\in [0, 1]$。当然读者也可以选择其他复杂的映射（激活函数）比如说 sigmoid 函数。映射过后我们就可以把 $\\tilde{y}^{k}$ 看作是我们估计出的标签（label）了。如果 $\\tilde{y}^{k}< 0.5$ 就对应标签 0，如果 $\\tilde{y}^{k}> 0.5$  就对应标签 1。 我们稍微复习一下整个流程，\n",
    "\n",
    "\n",
    "$$\n",
    "x^{k} \\rightarrow |\\psi_{\\rm in}\\rangle^{k} \\rightarrow U(\\boldsymbol{\\theta})|\\psi_{\\rm in}\\rangle^{k} \\rightarrow\n",
    "|\\psi_{\\rm out}\\rangle^{k} \\rightarrow ^{k}\\langle \\psi_{\\rm out} |Z\\otimes I\\cdots \\otimes I| \\psi_{\\rm out} \\rangle^{k}\n",
    "\\rightarrow \\langle Z \\rangle  \\rightarrow \\tilde{y}^{k}.\\tag{16}\n",
    "$$\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 损失函数\n",
    "\n",
    "相比于公式(1)中损失函数，需要在每次迭代中对所有 Ntrain 个数据点进行测量计算，在实际应用中，我们将训练集中的数据拆分为 \"Ntrain/BATCH\" 组，其中每组包含BATCH个数据。\n",
    "\n",
    "对第 i 组数据，训练对应损失函数：\n",
    "$$\n",
    "\\mathcal{L}_{i} = \\sum_{k=1}^{BATCH} \\frac{1}{BATCH} |y^{i,k} - \\tilde{y}^{i,k}|^2,\\tag{17}\n",
    "$$\n",
    "并对每一组训练 EPOCH 次。\n",
    "\n",
    "当取 \"BATCH = Ntrain\"，此时仅有一组数据点，Eq. (17)重新变为Eq. (1)。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-02T09:15:07.667491Z",
     "start_time": "2021-03-02T09:15:07.661325Z"
    }
   },
   "outputs": [],
   "source": [
    "# 生成只作用在第一个量子比特上的泡利 Z 算符\n",
    "# 其余量子比特上都作用单位矩阵\n",
    "def Observable(n):\n",
    "    r\"\"\"\n",
    "    :param n: 量子比特数量\n",
    "    :return: 局部可观测量: Z \\otimes I \\otimes ...\\otimes I\n",
    "    \"\"\"\n",
    "    Ob = pauli_str_to_matrix([[1.0, 'z0']], n)\n",
    "\n",
    "    return Ob"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 搭建整个优化流程图\n",
    "class Opt_Classifier(paddle_quantum.gate.Gate):\n",
    "    \"\"\"\n",
    "    创建模型训练网络\n",
    "    \"\"\"\n",
    "    def __init__(self, n, depth, seed_paras=1):\n",
    "        # 初始化部分，通过n, depth给出初始电路\n",
    "        super(Opt_Classifier, self).__init__()\n",
    "        self.n = n\n",
    "        self.depth = depth\n",
    "        # 初始化偏置 (bias)\n",
    "        self.bias = self.create_parameter(\n",
    "            shape=[1],\n",
    "            default_initializer=paddle.nn.initializer.Normal(std=0.01),\n",
    "            dtype='float32',\n",
    "            is_bias=False)\n",
    "        \n",
    "        self.circuit = Circuit(n)\n",
    "        # 先搭建广义的旋转层\n",
    "        for i in range(n):\n",
    "            self.circuit.rz(qubits_idx=i)\n",
    "            self.circuit.ry(qubits_idx=i)\n",
    "            self.circuit.rz(qubits_idx=i)\n",
    "\n",
    "        # 默认深度为 depth = 1\n",
    "        # 对每一层搭建电路\n",
    "        for d in range(3, depth + 3):\n",
    "            # 搭建纠缠层\n",
    "            for i in range(n-1):\n",
    "                self.circuit.cnot(qubits_idx=[i, i + 1])\n",
    "            self.circuit.cnot(qubits_idx=[n-1, 0])\n",
    "            # 对每一个量子比特搭建Ry\n",
    "            for i in range(n):\n",
    "                self.circuit.ry(qubits_idx=i)\n",
    "\n",
    "    # 定义前向传播机制、计算损失函数 和交叉验证正确率\n",
    "    def forward(self, state_in, label):\n",
    "        \"\"\"\n",
    "        输入： state_in：输入量子态，shape: [-1, 1, 2^n] -- 此教程中为[BATCH, 1, 2^n]\n",
    "               label：输入量子态对应标签，shape: [-1, 1]\n",
    "        计算损失函数:\n",
    "                L = 1/BATCH * ((<Z> + 1)/2 + bias - label)^2\n",
    "        \"\"\"\n",
    "        # 将 Numpy array 转换成 tensor\n",
    "        Ob = paddle.to_tensor(Observable(self.n))\n",
    "        label_pp = reshape(paddle.to_tensor(label), [-1, 1])\n",
    "\n",
    "        # 按照随机初始化的参数 theta \n",
    "        Utheta = self.circuit.unitary_matrix()\n",
    "\n",
    "        # 因为 Utheta是学习到的，我们这里用行向量运算来提速而不会影响训练效果\n",
    "        state_out = matmul(state_in, Utheta)  # [-1, 1, 2 ** n]形式，第一个参数在此教程中为BATCH\n",
    "\n",
    "        # 测量得到泡利 Z 算符的期望值 <Z> -- shape [-1,1,1]\n",
    "        E_Z = matmul(matmul(state_out, Ob), transpose(paddle.conj(state_out), perm=[0, 2, 1]))\n",
    "\n",
    "        # 映射 <Z> 处理成标签的估计值 \n",
    "        state_predict = paddle.real(E_Z)[:, 0] * 0.5 + 0.5 + self.bias  # 计算每一个y^{i,k}与真实值得平方差\n",
    "        loss = paddle.mean((state_predict - label_pp) ** 2)  # 对BATCH个得到的平方差取平均，得到L_i：shape:[1,1]\n",
    "\n",
    "        # 计算交叉验证正确率\n",
    "        is_correct = (paddle.abs(state_predict - label_pp) < 0.5).nonzero().shape[0]\n",
    "        acc = is_correct / label.shape[0]\n",
    "\n",
    "        return loss, acc, state_predict.numpy(), self.circuit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练过程\n",
    "\n",
    "好了， 那么定义完以上所有的概念之后我们不妨来看看实际的训练过程！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 用于绘制最终训练得到分类器的平面分类图\n",
    "def heatmap_plot(Opt_Classifier, N):\n",
    "    # 生成数据点 x_y_\n",
    "    Num_points = 30\n",
    "    x_y_ = []\n",
    "    for row_y in np.linspace(0.9, -0.9, Num_points):\n",
    "        row = []\n",
    "        for row_x in np.linspace(-0.9, 0.9, Num_points):\n",
    "            row.append([row_x, row_y])\n",
    "        x_y_.append(row)\n",
    "    x_y_ = np.array(x_y_).reshape(-1, 2).astype(\"float64\")\n",
    "\n",
    "    # 计算预测: heat_data\n",
    "    input_state_test = paddle.to_tensor(\n",
    "        datapoints_transform_to_state(x_y_, N))\n",
    "    loss_useless, acc_useless, state_predict, cir = Opt_Classifier(state_in=input_state_test, label=x_y_[:, 0])\n",
    "    heat_data = state_predict.reshape(Num_points, Num_points)\n",
    "\n",
    "    # 画图\n",
    "    fig = plt.figure(1)\n",
    "    ax = fig.add_subplot(111)\n",
    "    x_label = np.linspace(-0.9, 0.9, 3)\n",
    "    y_label = np.linspace(0.9, -0.9, 3)\n",
    "    ax.set_xticks([0, Num_points // 2, Num_points - 1])\n",
    "    ax.set_xticklabels(x_label)\n",
    "    ax.set_yticks([0, Num_points // 2, Num_points - 1])\n",
    "    ax.set_yticklabels(y_label)\n",
    "    im = ax.imshow(heat_data, cmap=plt.cm.RdBu)\n",
    "    plt.colorbar(im)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过 Adam 优化器不断学习训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def QClassifier(Ntrain, Ntest, gap, N, DEPTH, EPOCH, LR, BATCH, seed_paras, seed_data):\n",
    "    \"\"\"\n",
    "    量子二分类器\n",
    "    输入参数：\n",
    "        Ntrain,        # 规定训练集大小\n",
    "        Ntest,         # 规定测试集大小\n",
    "        gap,           # 设定决策边界的宽度\n",
    "        N,             # 所需的量子比特数量\n",
    "        DEPTH,         # 采用的电路深度\n",
    "        BATCH,         # 训练时 batch 的大小\n",
    "        EPOCH,         # 训练 epoch 轮数\n",
    "        LR,            # 设置学习速率\n",
    "        seed_paras,    # 设置随机种子用以初始化各种参数\n",
    "        seed_data,     # 固定生成数据集所需要的随机种子\n",
    "    \"\"\"\n",
    "    # 生成训练集测试集\n",
    "    train_x, train_y, test_x, test_y = circle_data_point_generator(Ntrain=Ntrain, Ntest=Ntest, boundary_gap=gap, seed_data=seed_data)\n",
    "    # 读取训练集的维度\n",
    "    N_train = train_x.shape[0]\n",
    "\n",
    "    paddle.seed(seed_paras)\n",
    "    # 初始化寄存器存储正确率 acc 等信息\n",
    "    summary_iter, summary_test_acc = [], []\n",
    "\n",
    "    # 一般来说，我们利用Adam优化器来获得相对好的收敛\n",
    "    # 当然你可以改成SGD或者是RMSprop\n",
    "    myLayer = Opt_Classifier(n=N, depth=DEPTH)  # 得到初始化量子电路\n",
    "    opt = paddle.optimizer.Adam(learning_rate=LR, parameters=myLayer.parameters())\n",
    "\n",
    "    # 优化循环\n",
    "    # 此处将训练集分为Ntrain/BATCH组数据，对每一组训练后得到的量子线路作为下一组数据训练的初始量子电路\n",
    "    # 故通过cir记录每组数据得到的最终量子线路\n",
    "    i = 0  # 记录总迭代次数\n",
    "    for ep in range(EPOCH):\n",
    "        # 将训练集分组，对每一组训练\n",
    "        for itr in range(N_train // BATCH):\n",
    "            i += 1  # 记录总迭代次数\n",
    "            # 将经典数据编码成量子态 |psi>, 维度 [BATCH, 2 ** N]\n",
    "            input_state = paddle.to_tensor(datapoints_transform_to_state(train_x[itr * BATCH:(itr + 1) * BATCH], N))\n",
    "\n",
    "            # 前向传播计算损失函数\n",
    "            loss, train_acc, state_predict_useless, cir \\\n",
    "                = myLayer(state_in=input_state, label=train_y[itr * BATCH:(itr + 1) * BATCH])  # 对此时量子电路优化\n",
    "            # 显示迭代过程中performance变化\n",
    "            if i % 30 == 5:\n",
    "                # 计算测试集上的正确率 test_acc\n",
    "                input_state_test = paddle.to_tensor(datapoints_transform_to_state(test_x, N))\n",
    "                loss_useless, test_acc, state_predict_useless, t_cir \\\n",
    "                    = myLayer(state_in=input_state_test,label=test_y)\n",
    "                print(\"epoch:\", ep, \"iter:\", itr,\n",
    "                      \"loss: %.4f\" % loss.numpy(),\n",
    "                      \"train acc: %.4f\" % train_acc,\n",
    "                      \"test acc: %.4f\" % test_acc)\n",
    "                # 存储正确率 acc 等信息\n",
    "                summary_iter.append(itr + ep * N_train)\n",
    "                summary_test_acc.append(test_acc) \n",
    "\n",
    "            # 反向传播极小化损失函数\n",
    "            loss.backward()\n",
    "            opt.minimize(loss)\n",
    "            opt.clear_grad()\n",
    "\n",
    "    # 得到训练后电路\n",
    "    print(\"训练后的电路：\")\n",
    "    print(cir)\n",
    "    # 画出 heatmap 表示的决策边界\n",
    "    heatmap_plot(myLayer, N=N)\n",
    "\n",
    "    return summary_test_acc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集的维度大小 x (200, 2) 和 y (200, 1)\n",
      "测试集的维度大小 x (100, 2) 和 y (100, 1) \n",
      "\n",
      "epoch: 0 iter: 4 loss: 0.2750 train acc: 0.7000 test acc: 0.6700\n",
      "epoch: 3 iter: 4 loss: 0.2471 train acc: 0.2500 test acc: 0.5500\n",
      "epoch: 6 iter: 4 loss: 0.1976 train acc: 0.8000 test acc: 0.9200\n",
      "epoch: 9 iter: 4 loss: 0.1639 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 12 iter: 4 loss: 0.1441 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 15 iter: 4 loss: 0.1337 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 18 iter: 4 loss: 0.1287 train acc: 1.0000 test acc: 1.0000\n",
      "训练后的电路：\n",
      "--Rz(3.490)----Ry(5.436)----Rz(3.281)----*--------------x----Ry(0.098)--\n",
      "                                         |              |               \n",
      "--Rz(1.499)----Ry(2.579)----Rz(3.496)----x----*---------|----Ry(1.282)--\n",
      "                                              |         |               \n",
      "--Rz(5.956)----Ry(3.158)----Rz(3.949)---------x----*----|----Ry(1.418)--\n",
      "                                                   |    |               \n",
      "--Rz(1.604)----Ry(0.722)----Rz(5.037)--------------x----*----Ry(2.437)--\n",
      "                                                                        \n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "主程序段总共运行了 7.0908989906311035 秒\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    \"\"\"\n",
    "    主函数\n",
    "    \"\"\"\n",
    "    time_start = time.time()\n",
    "    acc = QClassifier(\n",
    "        Ntrain = 200,        # 规定训练集大小\n",
    "        Ntest = 100,         # 规定测试集大小\n",
    "        gap = 0.5,           # 设定决策边界的宽度\n",
    "        N = 4,               # 所需的量子比特数量\n",
    "        DEPTH = 1,           # 采用的电路深度\n",
    "        BATCH = 20,          # 训练时 batch 的大小\n",
    "        EPOCH = int(200 * BATCH / Ntrain),          \n",
    "                             # 训练 epoch 轮数，使得总迭代次数 EPOCH * (Ntrain / BATCH) 在200左右\n",
    "        LR = 0.01,            # 设置学习速率\n",
    "        seed_paras = 19,     # 设置随机种子用以初始化各种参数\n",
    "        seed_data = 2,       # 固定生成数据集所需要的随机种子\n",
    "    )\n",
    "    \n",
    "    time_span = time.time() - time_start\n",
    "    print('主程序段总共运行了', time_span, '秒')\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过打印训练结果可以看到不断优化后分类器在测试集和训练集的正确率都达到了 $100\\%$。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 研究不同的编码方式\n",
    "\n",
    "监督学习的编码方式对分类结果有很大影响 [4]。在量桨中，我们集成了常用的编码方式，包括振幅编码、角度编码、IQP编码等。 用户可以用内置的 ``SimpleDataset`` 类实例对简单分类数据（不需要降维的数据）进行编码；也可以用内置的 ``VisionDataset`` 类实例对图片数据进行编码。编码的方法都是调用类对象的 ``encode`` 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'numpy.ndarray'>\n",
      "(100, 4)\n"
     ]
    }
   ],
   "source": [
    "# 使用前面构建的圆形数据集研究编码\n",
    "from paddle_quantum.dataset import *\n",
    "\n",
    "# 用两个量子比特编码二维数据\n",
    "quantum_train_x = SimpleDataset(2).encode(train_x, 'angle_encoding', 2)\n",
    "quantum_test_x = SimpleDataset(2).encode(test_x, 'angle_encoding', 2)\n",
    "\n",
    "print(type(quantum_test_x)) # ndarray\n",
    "print(quantum_test_x.shape) # (100, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们对上面的分类器进行化简，之后的所有分类都采用这个分类器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 简化的分类器\n",
    "def QClassifier2(quantum_train_x, train_y,quantum_test_x,test_y, N, DEPTH, EPOCH, LR, BATCH):\n",
    "    \"\"\"\n",
    "    量子二分类分类器\n",
    "    输入：\n",
    "        quantum_train_x     # 训练特征\n",
    "        train_y             # 训练标签\n",
    "        quantum_test_x      # 测试特征\n",
    "        test_y              # 测试标签\n",
    "        N                   # 使用的量子比特数目\n",
    "        DEPTH               # 分类器电路的深度\n",
    "        EPOCH               # 迭代次数\n",
    "        LR                  # 学习率\n",
    "        BATCH               # 一个批量的大小\n",
    "    \"\"\"\n",
    "    Ntrain = len(quantum_train_x)\n",
    "    \n",
    "    paddle.seed(1)\n",
    "\n",
    "    net = Opt_Classifier(n=N, depth=DEPTH)\n",
    "\n",
    "    # 测试准确率列表\n",
    "    summary_iter, summary_test_acc = [], []\n",
    "\n",
    "    # 这里用 Adam，但是也可以是 SGD 或者 RMSprop\n",
    "    opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())\n",
    "\n",
    "    # 进行优化\n",
    "    for ep in range(EPOCH):\n",
    "        for itr in range(Ntrain // BATCH):\n",
    "            # 导入数据\n",
    "            input_state = quantum_train_x[itr * BATCH:(itr + 1) * BATCH]  # paddle.tensor类型\n",
    "            input_state = reshape(input_state, [-1, 1, 2 ** N])\n",
    "            label = train_y[itr * BATCH:(itr + 1) * BATCH]\n",
    "            test_input_state = reshape(quantum_test_x, [-1, 1, 2 ** N])\n",
    "\n",
    "            loss, train_acc, state_predict_useless, cir = net(state_in=input_state, label=label)\n",
    "\n",
    "            if itr % 5 == 0:\n",
    "                # 获取测试准确率\n",
    "                loss_useless, test_acc, state_predict_useless, t_cir = net(state_in=test_input_state, label=test_y)\n",
    "                print(\"epoch:\", ep, \"iter:\", itr,\n",
    "                      \"loss: %.4f\" % loss.numpy(),\n",
    "                      \"train acc: %.4f\" % train_acc,\n",
    "                      \"test acc: %.4f\" % test_acc)\n",
    "                summary_test_acc.append(test_acc)\n",
    "\n",
    "            loss.backward()\n",
    "            opt.minimize(loss)\n",
    "            opt.clear_grad()\n",
    "\n",
    "    return summary_test_acc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在可以开始用不同编码方式对上面产生的圆形数据进行编码。这里我们采用五种编码方法：振幅编码、角度编码、泡利旋转编码、IQP编码、复杂纠缠编码。然后我们绘制出测试精度曲线以便分析。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Encoding method: amplitude_encoding\n",
      "epoch: 0 iter: 0 loss: 0.3005 train acc: 0.6000 test acc: 0.4600\n",
      "epoch: 0 iter: 5 loss: 0.2908 train acc: 0.3000 test acc: 0.5000\n",
      "epoch: 0 iter: 10 loss: 0.2313 train acc: 0.8000 test acc: 0.6200\n",
      "epoch: 0 iter: 15 loss: 0.2181 train acc: 0.7000 test acc: 0.7000\n",
      "Encoding method: angle_encoding\n",
      "epoch: 0 iter: 0 loss: 0.4141 train acc: 0.4000 test acc: 0.3700\n",
      "epoch: 0 iter: 5 loss: 0.2942 train acc: 0.6000 test acc: 0.6700\n",
      "epoch: 0 iter: 10 loss: 0.1952 train acc: 0.6000 test acc: 0.6700\n",
      "epoch: 0 iter: 15 loss: 0.2389 train acc: 0.6000 test acc: 0.6000\n",
      "Encoding method: pauli_rotation_encoding\n",
      "epoch: 0 iter: 0 loss: 0.1985 train acc: 0.7000 test acc: 0.7400\n",
      "epoch: 0 iter: 5 loss: 0.2303 train acc: 0.6000 test acc: 0.6900\n",
      "epoch: 0 iter: 10 loss: 0.1970 train acc: 0.6000 test acc: 0.7200\n",
      "epoch: 0 iter: 15 loss: 0.2120 train acc: 0.7000 test acc: 0.7000\n",
      "Encoding method: IQP_encoding\n",
      "epoch: 0 iter: 0 loss: 0.2962 train acc: 0.5000 test acc: 0.4500\n",
      "epoch: 0 iter: 5 loss: 0.2074 train acc: 0.7000 test acc: 0.7000\n",
      "epoch: 0 iter: 10 loss: 0.2463 train acc: 0.6000 test acc: 0.6500\n",
      "epoch: 0 iter: 15 loss: 0.2090 train acc: 0.9000 test acc: 0.5800\n",
      "Encoding method: complex_entangled_encoding\n",
      "epoch: 0 iter: 0 loss: 0.2500 train acc: 0.6000 test acc: 0.6800\n",
      "epoch: 0 iter: 5 loss: 0.2571 train acc: 0.5000 test acc: 0.6800\n",
      "epoch: 0 iter: 10 loss: 0.2661 train acc: 0.7000 test acc: 0.6700\n",
      "epoch: 0 iter: 15 loss: 0.1916 train acc: 0.8000 test acc: 0.7200\n"
     ]
    }
   ],
   "source": [
    "# 测试不同编码方式\n",
    "encoding_list = ['amplitude_encoding', 'angle_encoding', 'pauli_rotation_encoding', 'IQP_encoding', 'complex_entangled_encoding']\n",
    "num_qubit = 2 # 这里需要小心，如果量子比特数目取 1，可能会报错，因为有 CNOT 门\n",
    "dimension = 2\n",
    "acc_list = []\n",
    "\n",
    "for i in range(len(encoding_list)):\n",
    "    encoding = encoding_list[i]\n",
    "    print(\"Encoding method:\", encoding)\n",
    "    # 用 SimpleDataset 类来编码数据，这里数据维度为 2，编码量子比特数目也是 2\n",
    "    quantum_train_x= SimpleDataset(dimension).encode(train_x, encoding, num_qubit)\n",
    "    quantum_test_x= SimpleDataset(dimension).encode(test_x, encoding, num_qubit)\n",
    "    quantum_train_x = paddle.to_tensor(quantum_train_x)\n",
    "    quantum_test_x = paddle.to_tensor(quantum_test_x)\n",
    "\n",
    "    acc = QClassifier2(\n",
    "            quantum_train_x, # 训练特征\n",
    "            train_y,         # 训练标签\n",
    "            quantum_test_x,  # 测试特征\n",
    "            test_y,          # 测试标签\n",
    "            N = num_qubit,   # 使用的量子比特数目\n",
    "            DEPTH = 1,       # 分类器电路的深度\n",
    "            EPOCH = 1,       # 迭代次数\n",
    "            LR = 0.1,        # 学习率\n",
    "            BATCH = 10,      # 一个批量的大小\n",
    "          )\n",
    "    acc_list.append(acc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制五种编码方法的训练曲线\n",
    "x=[2*i for i in range(len(acc_list[0]))]\n",
    "for i in range(len(encoding_list)):\n",
    "    plt.plot(x,acc_list[i])\n",
    "plt.legend(encoding_list)\n",
    "plt.title(\"Benchmarking different encoding methods\")\n",
    "plt.xlabel(\"Iteration\")\n",
    "plt.ylabel(\"Test accuracy\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 用内置的 MNIST 和 Iris 数据集实现量子分类\n",
    "\n",
    "量桨将常用的分类数据集进行了编码，用户可以使用 `paddle_quantum.dataset` 模块获取编码的量子电路或者量子态。目前集成了4个数据集，包括 MNIST, FashionMNIST, Iris 和 BreastCancer。下面展示如何用这些内置数据集快速实现量子监督学习。\n",
    "\n",
    "我们从 Iris 数据集开始。Iris 数据集包括三种类别，每种类别有50个样本。数据集中只有四个特征，是比较简单且容易编码的数据集。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 0 iter: 0 loss: 0.3372 train acc: 0.5000 test acc: 0.5000\n",
      "epoch: 0 iter: 5 loss: 0.2687 train acc: 0.2500 test acc: 0.5500\n",
      "epoch: 0 iter: 10 loss: 0.0781 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 0 iter: 15 loss: 0.0786 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 1 iter: 0 loss: 0.0903 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 1 iter: 5 loss: 0.1020 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 1 iter: 10 loss: 0.0553 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 1 iter: 15 loss: 0.0559 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 2 iter: 0 loss: 0.0770 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 2 iter: 5 loss: 0.0879 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 2 iter: 10 loss: 0.0438 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 2 iter: 15 loss: 0.0538 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 3 iter: 0 loss: 0.0768 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 3 iter: 5 loss: 0.0887 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 3 iter: 10 loss: 0.0417 train acc: 1.0000 test acc: 1.0000\n",
      "epoch: 3 iter: 15 loss: 0.0511 train acc: 1.0000 test acc: 1.0000\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Iris 数据集二分类\n",
    "\n",
    "test_rate=0.2\n",
    "num_qubit=4\n",
    "\n",
    "# 获取 Iris 数据集的量子态\n",
    "iris = Iris (encoding='angle_encoding', num_qubits=num_qubit, test_rate=test_rate,classes=[0, 1], return_state=True)\n",
    "\n",
    "quantum_train_x, train_y = iris.train_x, iris.train_y\n",
    "quantum_test_x, test_y = iris.test_x, iris.test_y\n",
    "testing_data_num = len(test_y)\n",
    "training_data_num = len(train_y)\n",
    "\n",
    "acc = QClassifier2(\n",
    "        quantum_train_x, # 训练特征\n",
    "        train_y,         # 训练标签\n",
    "        quantum_test_x,  # 测试特征\n",
    "        test_y,          # 测试标签\n",
    "        N = num_qubit,   # 使用的量子比特数目\n",
    "        DEPTH = 1,       # 分类器电路的深度\n",
    "        EPOCH = 4,       # 迭代次数\n",
    "        LR = 0.1,        # 学习率\n",
    "        BATCH = 4,      # 一个批量的大小\n",
    "      )\n",
    "plt.plot(acc)\n",
    "plt.title(\"Classify Iris 0&1 using angle encoding\")\n",
    "plt.xlabel(\"Iteration\")\n",
    "plt.ylabel(\"Testing accuracy\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "第二个例子为 MNIST 数据集。 MNIST 是手写数字数据集，有 0-9 十个类别（每一类训练集中有 6000 个样本，测试集中有 1000 个样本）。所有的图片都是 $28\\times28$ 的灰度图，所以需要使用 ``resize`` 或 ``PCA`` 降维到目标维度 ``target_dimension`` 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 0 iter: 0 loss: 0.2345 train acc: 0.5750 test acc: 0.5350\n",
      "epoch: 0 iter: 5 loss: 0.2322 train acc: 0.6500 test acc: 0.5800\n",
      "epoch: 0 iter: 10 loss: 0.2423 train acc: 0.6250 test acc: 0.5550\n",
      "epoch: 1 iter: 0 loss: 0.1909 train acc: 0.8000 test acc: 0.6900\n",
      "epoch: 1 iter: 5 loss: 0.1938 train acc: 0.7250 test acc: 0.6450\n",
      "epoch: 1 iter: 10 loss: 0.2055 train acc: 0.6750 test acc: 0.7250\n",
      "epoch: 2 iter: 0 loss: 0.1855 train acc: 0.8000 test acc: 0.7400\n",
      "epoch: 2 iter: 5 loss: 0.1627 train acc: 0.8000 test acc: 0.7650\n",
      "epoch: 2 iter: 10 loss: 0.1684 train acc: 0.8250 test acc: 0.7900\n",
      "epoch: 3 iter: 0 loss: 0.1676 train acc: 0.8250 test acc: 0.7750\n",
      "epoch: 3 iter: 5 loss: 0.1387 train acc: 0.8500 test acc: 0.7500\n",
      "epoch: 3 iter: 10 loss: 0.1679 train acc: 0.8500 test acc: 0.7950\n",
      "epoch: 4 iter: 0 loss: 0.1584 train acc: 0.7250 test acc: 0.8050\n",
      "epoch: 4 iter: 5 loss: 0.1408 train acc: 0.8500 test acc: 0.8150\n",
      "epoch: 4 iter: 10 loss: 0.1603 train acc: 0.8500 test acc: 0.8100\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 使用 MNIST 进行分类\n",
    "\n",
    "# 主要参数\n",
    "training_data_num = 500\n",
    "testing_data_num = 200\n",
    "num_qubit = 4\n",
    "\n",
    "# 选择3和6两个类，将 MNIST 从 28*28 重采样为 4*4，再用振幅编码方式进行编码 \n",
    "train_dataset = MNIST(mode='train', encoding='amplitude_encoding', num_qubits=num_qubit, classes=[3,6],\n",
    "                      data_num=training_data_num,need_cropping=True,\n",
    "                      downscaling_method='resize', target_dimension=16, return_state=True)\n",
    "\n",
    "val_dataset = MNIST(mode='test', encoding='amplitude_encoding', num_qubits=num_qubit, classes=[3,6],\n",
    "                    data_num=testing_data_num,need_cropping=True,\n",
    "                    downscaling_method='resize', target_dimension=16,return_state=True)\n",
    "\n",
    "quantum_train_x, train_y = train_dataset.quantum_image_states, train_dataset.labels\n",
    "quantum_test_x, test_y = val_dataset.quantum_image_states, val_dataset.labels\n",
    "\n",
    "acc = QClassifier2(\n",
    "        quantum_train_x, # 训练特征\n",
    "        train_y,         # 训练标签\n",
    "        quantum_test_x,  # 测试特征\n",
    "        test_y,          # 测试标签\n",
    "        N = num_qubit,   # 使用的量子比特数目\n",
    "        DEPTH = 3,       # 分类器电路的深度\n",
    "        EPOCH = 5,       # 迭代次数\n",
    "        LR = 0.1,        # 学习率\n",
    "        BATCH = 40,      # 一个批量的大小\n",
    "      )\n",
    "plt.plot(acc)\n",
    "plt.title(\"Classify MNIST 3&6 using amplitude encoding\")\n",
    "plt.xlabel(\"Iteration\")\n",
    "plt.ylabel(\"Testing accuracy\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 参考文献\n",
    "\n",
    "[1] Mitarai, Kosuke, et al. Quantum circuit learning. [Physical Review A 98.3 (2018): 032309.](https://arxiv.org/abs/1803.00745)\n",
    "\n",
    "[2] Farhi, Edward, and Hartmut Neven. Classification with quantum neural networks on near term processors. [arXiv preprint arXiv:1802.06002 (2018).](https://arxiv.org/abs/1802.06002)\n",
    "\n",
    "[3] Schuld, Maria, et al. Circuit-centric quantum classifiers. [Physical Review A 101.3 (2020): 032308.](https://arxiv.org/abs/1804.00633)\n",
    "\n",
    "[4] Schuld, Maria. Supervised quantum machine learning models are kernel methods. [arXiv preprint arXiv:2101.11020 (2021).](https://arxiv.org/pdf/2101.11020)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
